package com.gitee.luosl.cola.match;

import com.gitee.luosl.cola.lang.Tuple;
import com.gitee.luosl.cola.lang.Tuple2;
import com.gitee.luosl.cola.lang.Tuple3;
import com.gitee.luosl.cola.lang.Tuple4;
import com.gitee.luosl.cola.match.cases.Case;
import com.gitee.luosl.cola.match.cases.ICase;
import com.gitee.luosl.cola.match.exception.MatchException;
import com.gitee.luosl.cola.match.exception.MatchWrapException;
import com.gitee.luosl.cola.util.Try;

public class Match<T> {

    private final T value;

    public Match(T value) {
        this.value = value;
    }

    @SuppressWarnings("unchecked")
    private <R> Try<R> doMatch(ICase<? extends T, ?, ? extends R>...cases){
        for(ICase<? extends T, ?, ? extends R> c: cases){
            final ICase<T, ?, R> cf = (ICase<T, ?, R>) c;
            if(cf.isDefinedAt(value)){
                return cf.collect(value);
            }
        }
        throw new MatchException("can not find match case!");
    }

    @SafeVarargs
    final public <R> R match(Case<? extends T, ?, ? extends R>...cases){
        Try<R> result = doMatch(cases);
        if(result.isSuccess()){
            return result.get();
        }else{
            throw (RuntimeException) result.getCause();
        }
    }

    @SafeVarargs
    final public <R> R matchCatch(ICase<? extends T, ?, ? extends R>...cases){
        Try<R> result = doMatch(cases);
        if(result.isFailure()){
            throw (RuntimeException) result.getCause();
        }else{
            return result.get();
        }
    }

    @SafeVarargs
    final public <R> Try<R> matchTry(ICase<? extends T, ?, ? extends R>...cases){
        Try<R> result = doMatch(cases);
        if (result.isFailure()) {
            Throwable throwable = result.getCause();
            if (throwable instanceof MatchWrapException) {
                return Try.failure(((MatchWrapException) throwable).getThrowable());
            }
        }
        return result;
    }

    public static <T> Match<T> of(T value) {
        return new Match<>(value);
    }

    public static <T1, T2> Match<Tuple2<T1, T2>> of(T1 value1, T2 value2) {
        return new Match<>(Tuple.of(value1, value2));
    }

    public static <T1, T2, T3> Match<Tuple3<T1, T2, T3>> of(T1 value1, T2 value2, T3 value3) {
        return new Match<>(Tuple.of(value1, value2, value3));
    }

    public static <T1, T2, T3, T4> Match<Tuple4<T1, T2, T3, T4>> of(T1 value1, T2 value2, T3 value3, T4 value4) {
        return new Match<>(Tuple.of(value1, value2, value3, value4));
    }

}
